home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / net_src.arc / smtpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-08  |  21.8 KB  |  996 lines

  1.  
  2. /*
  3.  *    Client routines for Simple Mail Transfer Protocol ala RFC821
  4.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  5.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  6.  *    Permission granted for non-commercial copying and use, provided
  7.  *    this notice is retained.
  8.  *     Modified 14 June 1987 by P. Karn for symbolic target addresses,
  9.  *    also rebuilt locking mechanism
  10.  *    Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
  11.  *    Added return of mail to sender as well as batching of commands 1/88 nn2z
  12.  */
  13. #include <stdio.h>
  14. #if    (!ATARI_ST || LATTICE)
  15. #include <fcntl.h>
  16. #endif
  17. #include <time.h>
  18. #ifdef UNIX
  19. #include <sys/types.h>
  20. #endif
  21. #include "global.h"
  22. #include "netuser.h"
  23. #include "mbuf.h"
  24. #include "timer.h"
  25. #include "tcp.h"
  26. #include "smtp.h"
  27. #include "trace.h"
  28. #include "cmdparse.h"
  29.  
  30. extern int16 lport;            /* local port placeholder */
  31. extern int32 resolve();
  32. static struct timer smtpcli_t;
  33. int32 gateway;
  34.  
  35. #ifdef SMTPTRACE
  36. int16    smtptrace = 0;            /* used for trace level */
  37. int dosmtptrace();
  38. #endif
  39.  
  40. int16    smtpmaxcli  = MAXSESSIONS;    /* the max client connections allowed */
  41. int16    smtpsessions = 0;        /* number of client connections
  42.                     * currently open */
  43. int16    smtpmode = 0;
  44.  
  45. static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  46.  
  47. static char quitcmd[] = "QUIT\r\n";
  48. static char eom[] = "\r\n.\r\n";
  49.  
  50. int smtptick(),dogateway(),dosmtpmaxcli(),mlock(),dotimer(),nextjob();
  51. int setsmtpmode(),sendwindow();
  52. void quit(),abort_trans(),retmail(),sendit(),del_session(),del_job();
  53. void execjobs(),smtp_transaction(),logerr();
  54. struct smtp_cb *newcb(),*lookup();
  55. struct smtp_job *setupjob();
  56.  
  57. struct cmds smtpcmds[] = {
  58.     "gateway",    dogateway,    0,    NULLCHAR,    NULLCHAR,
  59.     "mode",        setsmtpmode,    0,    NULLCHAR,    NULLCHAR,
  60.     "kick",        smtptick,    0,    NULLCHAR,    NULLCHAR,
  61.     "maxclients",    dosmtpmaxcli,    0,    NULLCHAR,    NULLCHAR,
  62.     "timer",    dotimer,    0,    NULLCHAR,    NULLCHAR,
  63. #ifdef SMTPTRACE
  64.     "trace",    dosmtptrace,    0,    NULLCHAR,    NULLCHAR,
  65. #endif
  66.     NULLCHAR,    NULLFP,        0,    
  67.     "subcommands: gateway kick maxclients timer trace",
  68.         NULLCHAR,
  69. };
  70.  
  71. dosmtp(argc,argv)
  72. int argc;
  73. char *argv[];
  74. {
  75.     return subcmd(smtpcmds,argc,argv);
  76. }
  77.  
  78. static int
  79. dosmtpmaxcli(argc,argv)
  80. int argc;
  81. char *argv[];
  82. {
  83.     int x;
  84.     if (argc < 2)
  85.         printf("%d\n",smtpmaxcli);
  86.     else {
  87.         x = atoi(argv[1]);
  88.         if (x > MAXSESSIONS)
  89.             printf("max clients must be <= %d\n",MAXSESSIONS);
  90.         else
  91.             smtpmaxcli = x;
  92.     }
  93.     return 0;
  94. }
  95.  
  96. static int
  97. setsmtpmode(argc,argv)
  98. int argc;
  99. char *argv[];
  100. {
  101.     if (argc < 2) {
  102.         printf("smtp mode: %s\n",
  103.             (smtpmode & QUEUE) ? "queue" : "route");
  104.     } else {
  105.         switch(*argv[1]) {
  106.         case 'q':
  107.             smtpmode |= QUEUE;
  108.             break;
  109.         case 'r':
  110.             smtpmode &= ~QUEUE;
  111.             break;
  112.         default:
  113.             printf("Usage: smtp mode [queue | route]\n");
  114.             break;
  115.         }
  116.     }
  117.     return 0;
  118. }
  119. static int
  120. dogateway(argc,argv)
  121. int argc;
  122. char *argv[];
  123. {
  124.     char *inet_ntoa();
  125.     int32 n;
  126.     extern char badhost[];
  127.  
  128.     if(argc < 2){
  129.         printf("%s\n",inet_ntoa(gateway));
  130.     } else if((n = resolve(argv[1])) == 0){
  131.         printf(badhost,argv[1]);
  132.         return 1;
  133.     } else
  134.         gateway = n;
  135.     return 0;
  136. }
  137.  
  138. #ifdef SMTPTRACE
  139. static int
  140. dosmtptrace(argc,argv)
  141. int argc;
  142. char *argv[];
  143. {
  144.     if (argc < 2)
  145.         printf("%d\n",smtptrace);
  146.     else 
  147.         smtptrace = atoi(argv[1]);
  148.     return 0;
  149. }
  150. #endif
  151.  
  152. /* Set outbound spool poll interval */
  153. static int
  154. dotimer(argc,argv)
  155. int argc;
  156. char *argv[];
  157. {
  158.     int smtptick();
  159.  
  160.     if(argc < 2){
  161.         printf("%lu/%lu\n",
  162.         (smtpcli_t.start - smtpcli_t.count)/(1000/MSPTICK),
  163.         smtpcli_t.start/(1000/MSPTICK));
  164.         return 0;
  165.     }
  166.     smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
  167.     smtpcli_t.arg = NULLCHAR;        /* dummy value */
  168.     smtpcli_t.start = atoi(argv[1])*(1000/MSPTICK);    /* set timer duration */
  169.     start_timer(&smtpcli_t);        /* and fire it up */
  170.     return 0;
  171. }
  172.  
  173. /* this is the routine that gets called every so often to do outgoing mail
  174.    processing */
  175. int
  176. smtptick()
  177. {
  178.     register struct smtp_cb *cb;
  179.     struct smtp_job *jp;
  180.     struct list *ap;
  181.     char    tmpstring[LINELEN], wfilename[13], prefix[9];
  182.     char    from[LINELEN], to[LINELEN];
  183.     char *cp, *cp1;
  184.     int32 destaddr;
  185.     FILE *wfile;
  186.  
  187. #ifdef SMTPTRACE
  188.     if (smtptrace > 5) {
  189.         printf("smtp daemon entered\n");
  190.         fflush(stdout);
  191.     }
  192. #endif
  193.     for(filedir(mailqueue,0,wfilename);wfilename[0] != '\0';
  194.         filedir(mailqueue,1,wfilename)){
  195.  
  196.         /* save the prefix of the file name which it job id */
  197.         cp = wfilename;
  198.         cp1 = prefix;
  199.         while (*cp && *cp != '.')
  200.             *cp1++ = *cp++;
  201.         *cp1 = '\0';
  202.  
  203.         /* lock this file from the smtp daemon */
  204.         if (mlock(mailqdir,prefix))
  205.             continue;
  206.  
  207.         sprintf(tmpstring,"%s/%s",mailqdir,wfilename);
  208.         if ((wfile = fopen(tmpstring,"r")) == NULLFILE) {
  209.             /* probably too many open files */
  210.             (void) rmlock(mailqdir,prefix);
  211.             /* continue to next message. The failure
  212.             * may be temporary */
  213.             continue;
  214.         }
  215.  
  216.         (void) fgets(tmpstring,LINELEN,wfile);    /* read target host */
  217.         rip(tmpstring);
  218.  
  219.         if ((destaddr = mailroute(tmpstring)) == 0) {
  220.             fclose(wfile);
  221.             printf("** smtp: Unknown address %s\n",tmpstring);
  222.             fflush(stdout);
  223.             (void) rmlock(mailqdir,prefix);
  224.             continue;
  225.         }
  226.  
  227.         if ((cb = lookup(destaddr)) == NULLCB) {
  228.             /* there are enough processes running already */
  229.             if (smtpsessions >= smtpmaxcli) {
  230. #ifdef SMTPTRACE
  231.                 if (smtptrace) {
  232.                     printf("smtp daemon: too many processes\n");
  233.                     fflush(stdout);
  234.                 }
  235. #endif
  236.                 fclose(wfile);
  237.                 (void) rmlock(mailqdir,prefix);
  238.                 break;
  239.             }
  240.             if ((cb = newcb()) == NULLCB) {
  241.                 fclose(wfile);
  242.                 (void) rmlock(mailqdir,prefix);
  243.                 break;
  244.             } 
  245.             cb->ipdest = destaddr;
  246.         } else {
  247.             /* This system is already is sending mail lets not
  248.             * interfere with its send queue.
  249.             */
  250.             if (cb->state != CLI_INIT_STATE) {
  251.                 fclose(wfile);
  252.                 (void) rmlock(mailqdir,prefix);
  253.                 continue;
  254.             }
  255.         }
  256.  
  257.         (void) fgets(from,LINELEN,wfile);    /* read from */
  258.         rip(from);
  259.         if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
  260.             fclose(wfile);
  261.             (void) rmlock(mailqdir,prefix);
  262.             del_session(cb);
  263.             break;
  264.         }
  265.         while (fgets(to,LINELEN,wfile) != NULLCHAR) {
  266.             rip(to);
  267.             if (addlist(&jp->to,to,DOMAIN) == NULLLIST) {
  268.                 fclose(wfile);
  269.                 del_session(cb);
  270.             }
  271.         }
  272.         fclose(wfile);
  273. #ifdef SMTPTRACE
  274.         if (smtptrace > 1) {
  275.             printf("queue job %s From: %s To:",prefix,from);
  276.             for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  277.                 printf(" %s",ap->val);
  278.             printf("\n");
  279.             fflush(stdout);
  280.         }
  281. #endif
  282.     }
  283.  
  284.     /* start sending that mail */
  285.     execjobs();
  286.  
  287.     /* Restart timer */
  288.     start_timer(&smtpcli_t);
  289. }
  290.  
  291. /* this is the master state machine that handles a single SMTP transaction */
  292. void
  293. smtp_transaction(cb)
  294. register struct smtp_cb *cb;
  295. {
  296.     void smtp_cts();
  297.     register char reply;
  298.     register struct list *tp;
  299.     int16 cnt;
  300.     struct mbuf *bp,*bpl;
  301.     char tbuf[LINELEN];
  302.     int rcode;
  303.  
  304. #ifdef SMTPTRACE
  305.     if (smtptrace > 5) 
  306.         printf("smtp_transaction() enter state=%u\n",cb->state);
  307.     if (smtptrace) {
  308.         printf("%s\n",cb->buf);
  309.         fflush(stdout);
  310.     }
  311. #endif
  312.     /* Another line follows; ignore this one */
  313.     if(cb->buf[0] == '0' || cb->buf[3] == '-')
  314.         return;
  315.  
  316.     reply = cb->buf[0];
  317.     rcode = atoi(cb->buf);
  318.  
  319.     /* if service shuting down */
  320.     if (rcode == 421) {
  321.         quit(cb);
  322.         return;
  323.     }
  324.  
  325.     switch(cb->state) {
  326.     case CLI_OPEN_STATE:
  327.         if (reply != '2')
  328.             quit(cb);
  329.         else {
  330.             cb->state = CLI_HELO_STATE;
  331.             sendit(cb,"HELO %s\r\nMAIL FROM:<%s>\r\n",
  332.             hostname,cb->jobq->from);
  333.         }
  334.         break;
  335.     case CLI_HELO_STATE:
  336.         if (reply != '2')
  337.             quit(cb);
  338.         else 
  339.             cb->state = CLI_MAIL_STATE;
  340.         break;            
  341.     case CLI_MAIL_STATE:
  342.         if (reply != '2')
  343.             quit(cb);
  344.         else {
  345.             cb->state = CLI_RCPT_STATE;
  346.             cb->rcpts = 0;
  347.             bpl = NULLBUF;
  348.             for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
  349.                 sprintf(tbuf,"RCPT TO:<%s>\r\n",tp->val);
  350.                 bp = qdata(tbuf,(int16)strlen(tbuf));
  351.                 if (bp == NULLBUF) {
  352.                     free_p(bpl);
  353.                     quit(cb);
  354.                     return;
  355.                 }
  356.                 append(&bpl,bp);
  357.                 cb->rcpts++;
  358. #ifdef SMTPTRACE
  359.                 if (smtptrace) {
  360.                     printf(">>> %s",tbuf);
  361.                     fflush(stdout);
  362.                 }
  363. #endif
  364.             }
  365.             send_tcp(cb->tcb,bpl);
  366.         }
  367.         break;
  368.     case CLI_RCPT_STATE:
  369.         if (reply == '5') {
  370.             logerr(cb);
  371.         } else if (reply == '2') {
  372.             cb->goodrcpt =1;
  373.         } else {
  374.             /* some kind of temporary failure */
  375.             abort_trans(cb);
  376.             break;
  377.         }
  378.         /* if more rcpts stay in this state */
  379.         if (--cb->rcpts != 0)
  380.             break;
  381.  
  382.         /* check for no good rcpt on the list */
  383.         if (cb->goodrcpt == 0) {
  384.             if (cb->errlog != NULLLIST)
  385.                 retmail(cb);
  386.             (void) unlink(cb->wname);    /* unlink workfile */
  387.             (void) unlink(cb->tname);    /* unlink text */
  388.             abort_trans(cb);
  389.             break;
  390.         }
  391.         /* if this file open fails abort */
  392.         if ((cb->tfile = fopen(cb->tname,"r")) == NULLFILE)
  393.             abort_trans(cb);
  394.         else {
  395.             /* optimize for slow packet links by sending
  396.              * DATA cmd and the 1st window of text
  397.              */
  398.             if (cb->tcb->window <= cb->tcb->sndcnt)
  399.                 cnt = 0;
  400.             else
  401.                 cnt = cb->tcb->window - cb->tcb->sndcnt;
  402.             bp = qdata("DATA\r\n",6);
  403.             cb->cts = 1;
  404.             cb->state = CLI_SEND_STATE;
  405.             if (sendwindow(cb,bp,cnt) == EOF)
  406.                 cb->cts = 0;
  407.         }
  408.         break;
  409.     case CLI_SEND_STATE:
  410.         if (reply == '3') {
  411.             cb->state = CLI_UNLK_STATE;
  412.         } else {
  413.             /* change cts to transmit upcall queueing more data */
  414.             cb->cts = 0;
  415.             quit(cb);
  416.         }
  417.         break;
  418.     case CLI_UNLK_STATE:
  419.         /* if a good transfer or permanent failure remove job */
  420.         if (reply == '2' || reply == '5') {
  421.             if (reply == '5')
  422.                 logerr(cb);
  423.             /* close and unlink the textfile */
  424.             if(cb->tfile != NULLFILE) {
  425.                 fclose(cb->tfile);
  426.                 cb->tfile = NULLFILE;
  427.             }
  428.             if (cb->errlog != NULLLIST)
  429.                 retmail(cb);
  430.             (void) unlink(cb->tname);
  431.             (void) unlink(cb->wname);    /* unlink workfile */
  432.             log(cb->tcb,"SMTP sent job %s To: %s From: %s",
  433.             cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  434.         }
  435.         if (nextjob(cb)) {
  436.             cb->state = CLI_MAIL_STATE;
  437.             sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  438.         } else 
  439.             /* the quit sent already in smtp_cts */
  440.             cb->state = CLI_QUIT_STATE;
  441.         break;
  442.     case CLI_IDLE_STATE:    /* used after a RSET and more mail to send */
  443.         if (reply != '2')
  444.             quit(cb);
  445.         else {
  446.             cb->state = CLI_MAIL_STATE;
  447.             sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  448.         }
  449.         break;            
  450.     case CLI_QUIT_STATE:
  451.         break;
  452.     }
  453. }
  454.  
  455. /* abort the currrent job.
  456.  * If more work exists set up the next job if
  457.  * not then shut down.
  458. */
  459. static void
  460. abort_trans(cb)
  461. register struct smtp_cb *cb;
  462. {
  463.     if(cb->tfile != NULLFILE) {
  464.         fclose(cb->tfile);
  465.         cb->tfile = NULLFILE;
  466.     }
  467.     if (nextjob(cb)) {
  468.         sendit(cb,"RSET\r\n");
  469.         cb->state = CLI_IDLE_STATE;
  470.     } else 
  471.         quit(cb);
  472. }
  473.  
  474. /* close down link after a failure */
  475. static void
  476. quit(cb)
  477. struct smtp_cb *cb;
  478. {
  479.     cb->state = CLI_QUIT_STATE;
  480.     sendit(cb,quitcmd);        /* issue a quit command */
  481.     close_tcp(cb->tcb);        /* close up connection */
  482. }
  483.  
  484. /* smtp receiver upcall routine.  fires up the state machine to parse input */
  485. static
  486. void
  487. smtp_rec(tcb,cnt)
  488. struct tcb *tcb;
  489. int16 cnt;
  490. {
  491.     register struct smtp_cb *cb;
  492.     char *inet_ntoa();
  493.     char c;
  494.     struct mbuf *bp;
  495.  
  496. #ifdef SMTPTRACE
  497.     if (smtptrace > 7)  {
  498.         printf("smtp_rec called\n");
  499.         fflush(stdout);
  500.     }
  501. #endif
  502.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  503.     recv_tcp(tcb,&bp,cnt);    /* suck up chars from low level routine */
  504.  
  505.     /* Assemble input line in buffer, return if incomplete */
  506.     while(pullup(&bp,&c,1) == 1) {
  507.         switch(c) {
  508.         case '\r':    /* strip cr's */
  509.             continue;
  510.         case '\n':    /* line is finished, go do it! */
  511.             cb->buf[cb->cnt] = '\0';
  512.             smtp_transaction(cb);
  513.             cb->cnt = 0;
  514.             break;
  515.         default:    /* other chars get added to buffer */
  516.             if(cb->cnt != LINELEN-1)
  517.                 cb->buf[cb->cnt++] = c;
  518.             break;
  519.         }
  520.     }
  521. }
  522.  
  523. /* smtp transmitter ready upcall routine.  twiddles cts flag */
  524. static 
  525. void
  526. smtp_cts(tcb,cnt)
  527. struct tcb *tcb;
  528. int16 cnt;
  529. {
  530.     register struct smtp_cb *cb;
  531.  
  532. #ifdef SMTPTRACE
  533.     if (smtptrace > 7) {
  534.         printf("smtp_cts called avail %d\n",cnt);
  535.         fflush(stdout);
  536.     }
  537. #endif
  538.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  539.  
  540.     /* don't do anything until/unless we're supposed to be sending */
  541.     if(cb->cts == 0)
  542.         return;
  543.  
  544.     if (sendwindow(cb,NULLBUF,cnt) == EOF)
  545.         cb->cts = 0;
  546. }
  547.  
  548. /* fill the rest of the window with data and send out the eof commands.
  549. * It is done this way to minimize the number of packets sent.
  550. */
  551. static int
  552. sendwindow(cb,ibp,cnt)
  553. register struct smtp_cb *cb;
  554. struct mbuf *ibp;
  555. int16 cnt;
  556. {
  557.     struct mbuf *bpl;
  558.     register struct mbuf *bp;
  559.     char *cp;
  560.     int c;
  561.     
  562.     bpl = ibp;
  563.     if((bp = alloc_mbuf(cnt)) == NULLBUF){
  564.         /* Hard to know what to do here */
  565.         return EOF;
  566.     }
  567.     cp = bp->data;
  568.     while(cnt > 1 && (c = getc(cb->tfile)) != EOF){
  569. #if (UNIX || MAC || AMIGA || ATARI_ST)
  570.         if(c == '\n'){
  571.             *cp++ = '\r';
  572.             bp->cnt++;
  573.             cnt--;
  574.         }
  575. #endif
  576.         *cp++ = c;
  577.         bp->cnt++;
  578.         cnt--;
  579.     }
  580.     append(&bpl,bp);
  581.     if (cnt > 1) {    /* EOF seen */
  582.         fclose(cb->tfile);
  583.         cb->tfile = NULLFILE;
  584.         /* send the end of data character. */
  585.         if (cnt < sizeof(eom) - 1) {
  586.             bp = qdata(eom,5);
  587.             append(&bpl,bp);
  588.             cnt = 0;    /* dont let anyone else in */
  589.         } else {
  590.             memcpy(&bp->data[bp->cnt],eom,sizeof(eom) - 1);
  591.             bp->cnt += sizeof(eom) - 1;
  592.             cnt -= sizeof(eom) - 1;
  593.         }
  594.         /* send the quit in this packet if last job */
  595.         if (cb->jobq->next == NULLJOB) {
  596.             if (cnt < sizeof(quitcmd) - 1) {
  597.                 bp = qdata(quitcmd,sizeof(quitcmd) - 1);
  598.                 append(&bpl,bp);
  599.             } else {
  600.                 memcpy(&bp->data[bp->cnt],
  601.                 quitcmd,sizeof(quitcmd) - 1);
  602.                 bp->cnt += sizeof(quitcmd) - 1;
  603.             }
  604.         }
  605.         send_tcp(cb->tcb,bpl);
  606.         if (cb->jobq->next == NULLJOB)
  607.             close_tcp(cb->tcb);    /* close up connection */
  608.         return EOF;
  609.     } else {
  610.         send_tcp(cb->tcb,bpl);
  611.         return 0;
  612.     }
  613. }
  614.  
  615. /* smtp state change upcall routine. */
  616. static
  617. void
  618. smtp_state(tcb,old,new)
  619. register struct tcb *tcb;
  620. char old,new;
  621. {
  622.     register struct smtp_cb *cb;
  623.     extern char *tcpstates[];
  624.  
  625. #ifdef SMTPTRACE
  626.     if (smtptrace > 7) {
  627.         printf("smtp_state called: %s\n",tcpstates[new]);
  628.         fflush(stdout);
  629.     }
  630. #endif
  631.     cb = (struct smtp_cb *)tcb->user;
  632.     switch(new) {
  633.     case ESTABLISHED:
  634.         cb->state = CLI_OPEN_STATE;    /* shouldn't be needed */
  635.         break;
  636.     case CLOSE_WAIT:
  637.         close_tcp(tcb);            /* shut things down */
  638.         break;
  639.     case CLOSED:
  640.         /* if this close was not done by us ie. a RST */
  641.         if(cb->tfile != NULLFILE)
  642.             fclose(cb->tfile);
  643.         del_session(cb);
  644.         del_tcp(tcb);
  645.         break;
  646.     }
  647. }
  648.  
  649. /* Send message back to server */
  650. /*VARARGS*/
  651. static void
  652. sendit(cb,fmt,arg1,arg2)
  653. struct smtp_cb *cb;
  654. char *fmt,*arg1,*arg2;
  655. {
  656.     struct mbuf *bp;
  657.     char tmpstring[256];
  658.  
  659. #ifdef SMTPTRACE
  660.     if (smtptrace) {
  661.         printf(">>> ");
  662.         printf(fmt,arg1,arg2);
  663.         fflush(stdout);
  664.     }
  665. #endif
  666.     sprintf(tmpstring,fmt,arg1,arg2);
  667.     bp = qdata(tmpstring,(int16)strlen(tmpstring));
  668.     send_tcp(cb->tcb,bp);
  669. }
  670.  
  671. /* create mail lockfile */
  672. int
  673. mlock(dir,id)
  674. char *dir,*id;
  675. {
  676.     char lockname[LINELEN];
  677.     int fd;
  678.     /* Try to create the lock file in an atomic operation */
  679.     sprintf(lockname,"%s/%s.lck",dir,id);
  680. #if    (ATARI_ST && !LATTICE)
  681.     if(!access(lockname,0) || (fd = creat(lockname, 0666)) == -1)
  682.         return -1;
  683. #else
  684.     if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600)) == -1)
  685.         return -1;
  686. #endif
  687.     close(fd);
  688.     return 0;
  689. }
  690.  
  691. /* remove mail lockfile */
  692. int
  693. rmlock(dir,id)
  694. char *dir,*id;
  695. {
  696.     char lockname[LINELEN];
  697.     sprintf(lockname,"%s/%s.lck",dir,id);
  698.     return(unlink(lockname));
  699. }
  700.  
  701. /* free the message struct and data */
  702. static void
  703. del_session(cb)
  704. register struct smtp_cb *cb;
  705. {
  706.     register struct smtp_job *jp,*tp;
  707.     register int i;
  708.  
  709.     if (cb == NULLCB)
  710.         return;
  711.     for(i=0; i<MAXSESSIONS; i++) 
  712.         if(cli_session[i] == cb) {
  713.             cli_session[i] = NULLCB;
  714.             break;
  715.         }
  716.  
  717.     if(cb->wname != NULLCHAR)
  718.         free(cb->wname);
  719.     if(cb->tname != NULLCHAR)
  720.         free(cb->tname);
  721.     for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
  722.             tp = jp->next;
  723.             del_job(jp);
  724.     }
  725.     del_list(cb->errlog);
  726.     free((char *)cb);
  727.     smtpsessions--;    /* number of connections active */
  728. }
  729.  
  730. void
  731. del_job(jp)
  732. register struct smtp_job *jp;
  733. {
  734.     if ( *jp->jobname != '\0')
  735.         (void) rmlock(mailqdir,jp->jobname);
  736.     if(jp->from != NULLCHAR)
  737.         free(jp->from);
  738.     del_list(jp->to);
  739.     free((char *)jp);
  740. }
  741.  
  742. /* delete a list of list structs */
  743. void
  744. del_list(lp)
  745. struct list *lp;
  746. {
  747.     register struct list *tp, *tp1;
  748.     for (tp = lp; tp != NULLLIST; tp = tp1) {
  749.             tp1 = tp->next;
  750.             if(tp->val != NULLCHAR)
  751.                 free(tp->val);
  752.             free((char *)tp);
  753.     }
  754. }
  755.  
  756. /* return message to sender */
  757. static void
  758. retmail(cb)
  759. struct smtp_cb *cb;
  760. {
  761.     register struct list *lp;
  762.     register FILE *tfile;
  763.     register int c;
  764.     FILE *infile,*tmpfile();
  765.     char *host,*to;
  766.     time_t t,time();
  767. #ifdef SMTPTRACE
  768.     if (smtptrace > 5) {
  769.         printf("smtp job %s returned to sender\n",cb->wname);
  770.         fflush(stdout);
  771.     }
  772. #endif
  773.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  774.     to = cb->jobq->from;
  775.     if (*to == '\0')
  776.         return;
  777.     if ((host = index(to,'@')) == NULLCHAR)
  778.         host = hostname;
  779.     else
  780.         host++;
  781.     if ((infile = fopen(cb->tname,"r")) == NULLFILE)
  782.         return;
  783.     if ((tfile = tmpfile()) == NULLFILE) {
  784.         fclose(infile);
  785.         return;
  786.     }
  787.     time(&t);
  788.     fprintf(tfile,"Date: %s",ptime(&t));
  789.     fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),hostname);
  790.     fprintf(tfile,"From: MAILER-DAEMON@%s\n",hostname);
  791.     fprintf(tfile,"To: %s\n",to);
  792.     fprintf(tfile,"Subject: Failed mail\n\n");
  793.     fprintf(tfile,"  ===== transcript follows =====\n\n");
  794.  
  795.     for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
  796.         fprintf(tfile,"%s\n",lp->val);
  797.  
  798.     fprintf(tfile,"\n  ===== Unsent message follows ====\n");
  799.  
  800.     while((c = getc(infile)) != EOF)
  801.         if (putc(c,tfile) == EOF)
  802.             break;
  803.     fclose(infile);
  804.     fseek(tfile,0L,0);
  805.     if ((smtpmode & QUEUE) != 0)
  806.         router_queue(cb->tcb,tfile,"",to);
  807.     else
  808.         queuejob(cb->tcb,tfile,host,to,"");
  809.     fclose(tfile);
  810. }
  811.  
  812. /* look to see if a smtp control block exists for this ipdest */
  813. static struct smtp_cb *
  814. lookup(destaddr)
  815. int32 destaddr;
  816. {
  817.     register int i;
  818.  
  819.     for(i=0; i<MAXSESSIONS; i++) {
  820.         if (cli_session[i] == NULLCB)
  821.             continue;
  822.         if(cli_session[i]->ipdest == destaddr)
  823.             return cli_session[i];
  824.     }
  825.     return NULLCB;
  826. }
  827.  
  828. /* create a new  smtp control block */
  829. static struct smtp_cb *
  830. newcb()
  831. {
  832.     register int i;
  833.     register struct smtp_cb *cb;
  834.  
  835.     for(i=0; i<MAXSESSIONS; i++) {
  836.         if(cli_session[i] == NULLCB) {
  837.             cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
  838.             if (cb == NULLCB)
  839.                 return(NULLCB);
  840.             cb->wname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  841.             if (cb->wname == NULLCHAR) {
  842.                 free((char *)cb);
  843.                 return(NULLCB);
  844.             }
  845.             cb->tname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  846.             if (cb->tname == NULLCHAR) {
  847.                 free(cb->wname);
  848.                 free((char *)cb);
  849.                 return(NULLCB);
  850.             }
  851.             cb->state = CLI_INIT_STATE;
  852.             cli_session[i] = cb;
  853.             smtpsessions++;    /* number of connections active */
  854.             return(cb);
  855.         }
  856.     }
  857.     return NULLCB;
  858. }
  859.  
  860. static void
  861. execjobs()
  862. {
  863.     struct socket lsocket, fsocket;
  864.     void smtp_rec(), smtp_cts(), smtp_state();
  865.     register struct smtp_cb *cb;
  866.     register int i;
  867.  
  868.     for(i=0; i<MAXSESSIONS; i++) {
  869.         cb = cli_session[i];
  870.         if (cb == NULLCB) 
  871.             continue;
  872.         if(cb->state != CLI_INIT_STATE)
  873.             continue;
  874.  
  875.         sprintf(cb->tname,"%s/%s.txt",mailqdir,cb->jobq->jobname);
  876.         sprintf(cb->wname,"%s/%s.wrk",mailqdir,cb->jobq->jobname);
  877.  
  878.         /* setup the socket */
  879.         fsocket.address = cb->ipdest;
  880.         fsocket.port = SMTP_PORT;
  881.         lsocket.address = ip_addr;    /* our ip address */
  882.         lsocket.port = lport++;        /* next unused port */
  883. #ifdef SMTPTRACE
  884.         if (smtptrace) {
  885.             printf("Trying Connection to %s\n",inet_ntoa(fsocket.address));
  886.             fflush(stdout);
  887.         }
  888. #endif
  889.  
  890.         /* open smtp connection */
  891.         cb->state = CLI_OPEN_STATE;    /* init state placeholder */
  892.         cb->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,tcp_window,
  893.             smtp_rec,smtp_cts,smtp_state,0,(char *)cb);
  894.         cb->tcb->user = (char *)cb;    /* Upward pointer */
  895.     }
  896. }
  897.     
  898. /* add this job to control block queue */
  899. struct smtp_job *
  900. setupjob(cb,id,from)
  901. struct smtp_cb *cb;
  902. char *id,*from;
  903. {
  904.     register struct smtp_job *p1,*p2;
  905.  
  906.     p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
  907.     if (p1 == NULLJOB)
  908.         return NULLJOB;
  909.     p1->from = malloc((unsigned)strlen(from) + 1);
  910.     if (p1->from == NULLCHAR) {
  911.         free((char *)p1);
  912.         return NULLJOB;
  913.     }
  914.     strcpy(p1->from,from);
  915.     strcpy(p1->jobname,id);
  916.     /* now add to end of jobq */
  917.     if ((p2 = cb->jobq) == NULLJOB)
  918.         cb->jobq = p1;
  919.     else {
  920.         while(p2->next != NULLJOB)
  921.             p2 = p2->next;
  922.         p2->next = p1;
  923.     }
  924.     return p1;
  925. }
  926.  
  927. /* called to advance to the next job */
  928. static int
  929. nextjob(cb)
  930. register struct smtp_cb *cb;
  931. {
  932.     register struct smtp_job *jp;
  933.  
  934.  
  935.     jp = cb->jobq->next;
  936.     del_job(cb->jobq);
  937.     if (jp == NULLJOB) {
  938.         cb->jobq = NULLJOB;
  939.         return 0;
  940.     }
  941.     /* remove the error log of previous message */
  942.     del_list(cb->errlog);
  943.     cb->errlog = NULLLIST;
  944.     cb->goodrcpt = 0;
  945.     cb->jobq = jp;
  946.     sprintf(cb->tname,"%s/%s.txt",mailqdir,jp->jobname);
  947.     sprintf(cb->wname,"%s/%s.wrk",mailqdir,jp->jobname);
  948. #ifdef SMTPTRACE
  949.     if (smtptrace > 5) {
  950.         printf("sending job %s\n",jp->jobname);
  951.         fflush(stdout);
  952.     }
  953. #endif
  954.         return 1;
  955.  
  956. }
  957.  
  958.  
  959. /* mail routing funtion. For now just used the hosts file */
  960. int32
  961. mailroute(dest)
  962. char *dest;
  963. {
  964.     int32 destaddr;
  965.  
  966.     /* look up address or use the gateway */
  967.     if ((destaddr = resolve(dest)) == 0)
  968.         if (gateway != 0) 
  969.             destaddr = gateway; /* Use the gateway  */
  970.     return destaddr;
  971.     
  972. }
  973.  
  974. /* save error reply for in error list */
  975. static void
  976. logerr(cb)
  977. struct smtp_cb *cb;
  978. {
  979.     register struct list *lp,*tp;
  980.     if ((tp = (struct list *)calloc(1,sizeof(struct list))) == NULLLIST)
  981.         return;
  982.     if ((tp->val = malloc((unsigned)strlen(cb->buf)+1)) == NULLCHAR) {
  983.         free((char *)tp);
  984.         return;
  985.     }
  986.     /* find end of list */
  987.     if ((lp = cb->errlog) == NULLLIST)
  988.         cb->errlog = tp;
  989.     else {
  990.         while(lp->next != NULLLIST)
  991.             lp = lp->next;
  992.         lp->next = tp;
  993.     }
  994.     strcpy(tp->val,cb->buf);
  995. }
  996.